package cn.coder.jdbc.config;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Properties;
import java.util.Set;

import javax.sql.DataSource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cn.coder.jdbc.core.JdbcDataSource;

public class ResourceConfigFactory {
	private static final Logger logger = LoggerFactory.getLogger(ResourceConfigFactory.class);

	private static final String DATASOURCE_PREFIX = "jdbc.datasource.";
	private static final int DATASOURCE_PREFIX_LENGTH = DATASOURCE_PREFIX.length();
	private JdbcConfig jdbcConfig;
	private HashMap<String, Properties> dataSources = new HashMap<>();

	public ResourceConfigFactory() {
		this.jdbcConfig = new JdbcConfig();
	}

	public JdbcConfig parse(String config) {
		Properties p = loadProperties(config);
		if (!p.isEmpty()) {
			parseProperties(p);
		}
		printConfig();
		return this.jdbcConfig;
	}

	private Properties loadProperties(String config) {
		Properties p = new Properties();
		InputStream input = null;
		try {
			input = this.getClass().getClassLoader().getResourceAsStream(config);
			if (input != null) {
				p.load(input);
			}
		} catch (IOException e) {
			logger.error("Load '" + config + "' faild", e);
		} finally {
			if (input != null) {
				try {
					input.close();
				} catch (IOException e) {
				}
			}
		}
		return p;
	}

	private void parseProperties(Properties p) {
		Set<Object> keys = p.keySet();
		for (Object key : keys) {
			if (key.toString().startsWith(DATASOURCE_PREFIX)) {
				parseDataSourceProperty(key.toString(), p);
			} else {
				parseProperty(key.toString(), p);
			}
		}
		parseDataSource();
	}

	private void printConfig() {
		if (logger.isDebugEnabled()) {
			final JdbcConfig config = this.jdbcConfig;
			StringBuilder sb = new StringBuilder();
			sb.append("\n====================  jdbcconfig  ====================");
			sb.append("\njdbc.queryTimeout                 ").append(config.getQueryTimeout());
			sb.append("\njdbc.multiQueries                 ").append(config.isMultiQueries());
			for (Properties ps : this.dataSources.values()) {
				sb.append("\n====================  dataSource  ====================");
				sb.append("\njdbc.sourceName                   ").append(ps.getProperty(DATASOURCE_PREFIX + "name"));
				sb.append("\njdbc.driverClassName              ")
						.append(ps.getProperty(DATASOURCE_PREFIX + "driverClassName"));
				sb.append("\njdbc.url                          ").append(ps.getProperty(DATASOURCE_PREFIX + "url"));
				sb.append("\njdbc.username                     ")
						.append(ps.getProperty(DATASOURCE_PREFIX + "username"));
				sb.append("\njdbc.password                     ")
						.append(ps.getProperty(DATASOURCE_PREFIX + "password"));
				sb.append("\njdbc.initialSize                  ")
						.append(ps.getProperty(DATASOURCE_PREFIX + "initialSize"));
			}
			logger.debug(sb.toString());
		}
	}

	private void parseDataSourceProperty(String key, Properties p) {
		int end = key.indexOf(".", DATASOURCE_PREFIX_LENGTH);
		String source = (end == -1) ? "default" : key.substring(DATASOURCE_PREFIX_LENGTH, end);

		String value = p.getProperty(key);

		// 不是默认数据源，移除数据源名称
		if (!"default".equals(source)) {
			key = DATASOURCE_PREFIX + key.substring(end + 1);
		}

		Properties ps = dataSources.get(source);
		if (ps == null) {
			ps = new Properties();
			ps.put(DATASOURCE_PREFIX + "name", source);// 数据源名称
			dataSources.put(source, ps);
		}

		if (key.equals(DATASOURCE_PREFIX + "url") 
				&& this.jdbcConfig.isMultiQueries()
				&& value.indexOf("allowMultiQueries") == -1) {
			value = value.concat(value.contains("?") ? "&" :"?" + "allowMultiQueries=true");
		}

		ps.put(key, value);
	}

	private void parseDataSource() {
		if (!dataSources.isEmpty()) {
			logger.debug("Find {} datasources.", dataSources.size());
			Set<String> keys = this.dataSources.keySet();
			DataSource ds;
			for (String key : keys) {
				ds = new JdbcDataSource(this.dataSources.get(key));
				this.jdbcConfig.addDataSource(key, ds);
				logger.debug("Datasource '{}' created.", key);
			}
		}
	}

	private void parseProperty(String key, Properties p) {
		if ("jdbc.queryTimeout".equals(key)) {
			int qt = Integer.parseInt(p.getProperty(key, "5"));
			this.jdbcConfig.setQueryTimeout(qt);
		} else if ("jdbc.multiQueries".equals(key)) {
			boolean mq = Boolean.parseBoolean(p.getProperty(key, "true"));
			this.jdbcConfig.setMultiQueries(mq);
		}
	}

}
